// RMSWaveSocket.cpp: implementation of the CRMSWaveSocket class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "RMSBaseServer.h"
#include "RMSWaveSocket.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

// Global data
#define	PACKET_COUNT 255

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////


#ifdef _MYDEBUG_ // #J7070010 - TOMOKOH
#define mydebug
#endif
// debug begin
#include <string>
using namespace std;
#ifdef mydebug
CMyDebug md("debug_serv.log");
#endif
// debug end




int EnableFEC = 0;
int
  FECparam1=0,
  FECparam2=0;

#include <string>
using namespace std;

// == CTimedSending ==

#ifdef mydebug
  LARGE_INTEGER pfreq;
#endif

CTimedSending::CTimedSending(SOCKET udpsock, int interval, int maxqueue) : TheTimer(interval), maxpackets(maxqueue)
{
#ifdef mydebug
  QueryPerformanceFrequency(&pfreq);
#endif
  hOutside = CreateEvent(NULL, FALSE, TRUE, NULL);
  InitializeCriticalSection(&cs);
  EmptyCall = false;
  Start();
}
CTimedSending::~CTimedSending()
{
  Stop();
  WaitForSingleObject(hOutside, 5000);
  CloseHandle(hOutside);
  DeleteCriticalSection(&cs);
}
void CTimedSending::Call()
{
  ResetEvent(hOutside);
  EnterCriticalSection(&cs);
  if(sock!=0)
  {
    if ( packets.size()>0 )
    {
      sendto(sock, (char*) packets.front().data, packets.front().length, 0, (sockaddr*)& packets.front().toaddr, sizeof(SOCKADDR_IN));
      Sleep(1);
#ifdef mydebug
static char buf[64];
static LARGE_INTEGER t1,t2;
t1=t2;
QueryPerformanceCounter(&t2);
double t = (double)(t2.QuadPart-t1.QuadPart)/pfreq.QuadPart;
sprintf(buf, "%.5f (%d)",t,packets.size());
md.printstr(string(buf));
#endif
      packets.pop();
      EmptyCall = false;
    } else EmptyCall=true;
  }
  LeaveCriticalSection(&cs);
  SetEvent(hOutside);
}
void CTimedSending::AddData(unsigned char* pdata, int len, SOCKADDR_IN ip)
{
  EnterCriticalSection(&cs);
    if (packets.size()<=maxpackets)
      packets.push(CUDPPacket(pdata, len, ip));
    if (EmptyCall || (packets.size()>maxpackets/2)) ComeOn();
  LeaveCriticalSection(&cs);
}


// == CRMSWaveSocket ==

CRMSWaveSocket::CRMSWaveSocket(CBaseServer* p, CRMSClientSocket* pClient)
{
	m_bAudio = TRUE;
	m_bVideo = FALSE;

           pFEC = NULL;
           pSending = NULL;
           if (EnableFEC!=0)
           {
             pFEC = new CSimpleFEC(FECparam1, FECparam2, (unsigned int)TAG_RMSSOUND_v01, (unsigned int) TAG_RMSFEC_PSHUF1, EnableFEC==2);
             //  CSimpleFEC(int aMaxBytes, int aShuffleNo, unsigned int aTag, unsigned int aTag2);
             if(pFEC->ErrorText!="")
             {
               string s = "FEC ERROR: "+pFEC->ErrorText;
               MessageBox(NULL, s.c_str(), "FEC ERROR", MB_OK);
               exit(-1);
             }
             pSending = new CTimedSending(0, pFEC->GetInterval(), pFEC->GetMaxQueue());
//             printf("INFO: recommended time lag = %d ms\n", sf.TimeLag);
//             printf("INFO: packet rate = %.1f packets/sec\n", sf.PacketRate);
           }

	this->pClient = pClient;
	pBaseServer=p;
	m_bActive = true;
	NewPassword = false;
	m_pData = new BYTE[0x20000];
	CameraID	=0;
	StartTime=0;
	Samples=0;
	m_udpSocket=0;
	ResetData();
}

CRMSWaveSocket::~CRMSWaveSocket()
{
	Terminate(TRUE);
	if(m_pData) delete[] m_pData;
	if(pFEC!=NULL) delete pFEC;
    if(pSending!=NULL) delete pSending;
}

DWORD CRMSWaveSocket::OnTerminate()
{
	m_eventNewFrame.SetEvent(); // set event
	m_eventNewWave.SetEvent(); // set event
	CloseSession();
	return DEFAULT_TERMINATION_TIMEOUT;
}

void CRMSWaveSocket::ResetData()
{
	pSzAudio=pBaseServer->GetDataPtr_SizeAudio(CameraID);
	dtAudio=pBaseServer->GetDataPtr_dtAudio(CameraID);
	pCriticalAudio=pBaseServer->GetDataPtr_CriticalAudio(CameraID);
	m_eventNewWave.SetEvent(); // set event

	m_frames = 0;
	pSzVideo = pBaseServer->GetDataPtr_SizeVideo(CameraID);
	dtVideo = pBaseServer->GetDataPtr_dtVideo(CameraID);
	pCriticalData = pBaseServer->GetDataPtr_CriticalData(CameraID);
	m_eventNewFrame.SetEvent(); // set event
}

#define VIDEO_PACKET_SIZE	1024


DWORD CRMSWaveSocket::ThreadFunc()
{
	TRACE1("%x - CRMSWaveSocket::ThreadFunc() - started\n", this);
	int nLen,L;
	DWORD dwTag;
	BYTE byPacketCounter;
	BYTE* pSrc;

	int length;
	BYTE* pPacket;
	int ecnt = 0;
	int frameLength = 0;
	BYTE* pFrame = NULL;
	HANDLE events[2] = {NULL, NULL};
	DWORD audioReady = WAIT_OBJECT_0;
	DWORD videoReady = WAIT_OBJECT_0+1;

	SetThreadPriority(m_hThread,THREAD_PRIORITY_ABOVE_NORMAL);
	LINGER linger = { 1,0 };
	setsockopt(m_udpSocket,SOL_SOCKET,SO_LINGER,(LPCTSTR)&linger,sizeof(linger));

	pSrc = m_pData;
	nLen = sizeof(SOCKADDR_IN);
	if(recvfrom(m_udpSocket, (char*)pSrc, 0x20000, 0, (LPSOCKADDR)&m_saClient, &nLen) == SOCKET_ERROR)
	{
		goto exit;
	}
	memcpy(&dwTag, pSrc, sizeof(DWORD)); pSrc += sizeof(DWORD);
	if(dwTag != TAG_SOUNDUDPPORT)
	{
		goto exit;
	}

	if(m_bAudio)
	{
		ecnt++;
		audioReady = WAIT_OBJECT_0;
		events[0] = m_eventNewWave;
	}
	if(m_bVideo)
	{
		ecnt++;
		videoReady = WAIT_OBJECT_0;
		events[0] = m_eventNewFrame;
	}
	if(ecnt == 2)
	{
		audioReady = WAIT_OBJECT_0;
		videoReady = WAIT_OBJECT_0+1;
		events[0] = m_eventNewWave;
		events[1] = m_eventNewFrame;
	}

	byPacketCounter = 0;
	while(!m_bQuit)
	{
		DWORD wobjects;

		while(!m_bQuit)
		{
			wobjects = WaitForMultipleObjects(ecnt, events, FALSE, 500);
			if(wobjects != WAIT_TIMEOUT)
				break;
		}

		if(m_bQuit)
			break;

		if(m_bAudio && (wobjects == audioReady))
		{ // send audiodata
			if(m_dwTypeAudio == TAG_RMSSOUND_v01)
			{
				EnterCriticalSection(pCriticalAudio);
				L = *pSzAudio;

                                            if(pFEC!=NULL)
                                            {
                                              pFEC->AddSamples(dtAudio, L);
                                              LeaveCriticalSection(pCriticalAudio);
                                              while(L) {if(++byPacketCounter >= PACKET_COUNT) byPacketCounter = 0; L -= 33;}

                                              nLen=0;
                                              int no;
                                              while(pFEC->GetPacket(m_pData, &no))
                                              {
                                                pSending->AddData(m_pData, no, m_saClient);
/*                                                Sleep(33);
                                                int sent;
                                                if((sent=sendto(m_udpSocket, (char*)m_pData, no, 0, (LPSOCKADDR)&m_saClient, sizeof(SOCKADDR_IN))) == SOCKET_ERROR)
                                                {
                                                  TRACE0("Unable to send audio data to client\n");
                                                  goto exit;
                                                }*/
//md.printsome("("+md.inttostr(sent)+" of "+md.inttostr(no)+")", m_pData, 100, no);
                                                nLen+=no;
                                              }
                                            } else
                                            {
                                              memcpy(m_pData + (2 * sizeof(DWORD) + sizeof(BYTE)), dtAudio, L);
                                              LeaveCriticalSection(pCriticalAudio);
                                              memcpy(m_pData, &m_dwTypeAudio, sizeof(DWORD));
                                              memcpy(m_pData + sizeof(DWORD), &byPacketCounter, sizeof(BYTE));
                                              memcpy(m_pData + (sizeof(DWORD) + sizeof(BYTE)), &L, sizeof(DWORD));
                                              nLen = L + (8 + 1);
                                              while(L)
                                              {
                                                if(++byPacketCounter >= PACKET_COUNT) byPacketCounter = 0;
                                                L -= 33; // could be few audio packets
                                              }
                                              if(sendto(m_udpSocket, (char*)m_pData, nLen, 0, (LPSOCKADDR)&m_saClient, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
                                              {
                                                TRACE0("Unable to send audio data to client\n");
                                                goto exit;
                                              }
                                            }
				//pClient->bitrateShaper.countUDPPacketData(nLen);
#ifdef _RMS_TRANSLATOR_
				pBaseServer->SaveCameraAudio(CameraID, *(m_pData+sizeof(DWORD)));
#endif //def _RMS_TRANSLATOR_
			}
		}
		else if(m_bVideo && (wobjects == videoReady))
		{ // there is a new frame to send
			EnterCriticalSection(pCriticalData);
			length = *pSzVideo;
			if((frameLength < length) || (pFrame == NULL))
			{
				if(pFrame != NULL)
					delete pFrame;
				frameLength = length;
				pFrame = new BYTE[frameLength+(3*sizeof(DWORD))];
				if(pFrame == NULL)
				{
					LeaveCriticalSection(pCriticalData);
					goto exit;
				}
			}
			memcpy(pFrame + (3 * sizeof(DWORD)), dtVideo, length);
			pPacket = pFrame;
			LeaveCriticalSection(pCriticalData);

			nLen = 0;
			m_frames++;
			while(!m_bQuit || (length <= nLen))
			{ // send a fragment of video frame
				int ln = length - nLen;
				int millis = 0;
				DWORD sz;

				if(ln > VIDEO_PACKET_SIZE) ln = VIDEO_PACKET_SIZE;
				if(pClient->bitrateShaper.isItTimeToSendByUDP(&millis, ln + (3 * sizeof(DWORD))))
				{
					sz = MAKELONG(LOWORD(length), LOWORD(nLen));
					memcpy(pPacket, &m_dwTypeVideo, sizeof(DWORD));
					memcpy(pPacket + sizeof(DWORD), &m_frames, sizeof(DWORD));
					memcpy(pPacket + (2 * sizeof(DWORD)), &sz, sizeof(DWORD));
					if(sendto(m_udpSocket, (char*)pPacket, ln + (3 * sizeof(DWORD)), 0,
							  (LPSOCKADDR)&m_saClient, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
					{
						TRACE0("Unable to send video data to client\n");
						goto exit;
					}
					pClient->bitrateShaper.countUDPPacketData(ln + (3 * sizeof(DWORD)));
					if(ln == 0)
						break;

					pPacket += ln;
					nLen += ln;

					if(m_bQuit)
						goto exit;
				}

				if(m_bAudio && (WaitForSingleObject(m_eventNewWave, millis) == WAIT_OBJECT_0))
				{ // send audiodata
					if(m_dwTypeAudio == TAG_RMSSOUND_v01)
					{
						EnterCriticalSection(pCriticalAudio);
						L = *pSzAudio;
						memcpy(m_pData + (2 * sizeof(DWORD) + sizeof(BYTE)), dtAudio, L);
						LeaveCriticalSection(pCriticalAudio);
						memcpy(m_pData, &m_dwTypeAudio, sizeof(DWORD));
						memcpy(m_pData + sizeof(DWORD), &byPacketCounter, sizeof(BYTE));
						memcpy(m_pData + (sizeof(DWORD) + sizeof(BYTE)), &L, sizeof(DWORD));
						ln = L + (8 + 1);
						while(L)
						{
							if(++byPacketCounter >= PACKET_COUNT) byPacketCounter = 0;
							L -= 33; // could be few audio packets
						}
						if(sendto(m_udpSocket, (char*)m_pData, ln, 0,
								  (LPSOCKADDR)&m_saClient, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
						{
							TRACE0("Unable to send audio data to client\n");
							goto exit;
						}
						pClient->bitrateShaper.countUDPPacketData(ln);
					}
				}
			}
		}
	}

exit:
	if(pFrame != NULL) delete pFrame;
	CloseSession();
	TRACE1("%x - CRMSWaveSocket::ThreadFunc() - finished\n", this);
	return 0;
}

int CRMSWaveSocket::StartWaveSession(SOCKADDR_IN saServer, BOOL bAudio/* = TRUE*/, BOOL bVideo/* = FALSE*/)
{
	if (m_hThread) {
		printf("error");
	}

	m_bAudio = bAudio;
	m_bVideo = bVideo;

	ResetData();
	m_saServer = saServer;

	m_udpSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
	if(m_udpSocket == INVALID_SOCKET)
	{
		TRACE0("Unable to create udp socket\n");
		CloseSession();
		return FALSE;
	}
	LINGER linger = { 1,0 };
	//Fixes Bug in MFC Socket Impl
	setsockopt(m_udpSocket,SOL_SOCKET,SO_LINGER,(LPCTSTR)&linger,sizeof(linger));

	// should not use same port as TCP !!!
	bool binded = false;
	//m_saServer.sin_port+=100;		
	m_saServer.sin_port=htons(ntohs(m_saServer.sin_port)+100);

	for (int i=0;i<500;i++) 
	{
		if(bind(m_udpSocket, (LPSOCKADDR)&m_saServer, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
		{
			//m_saServer.sin_port++;
			m_saServer.sin_port=htons(ntohs(m_saServer.sin_port)+1);
		}
		else
		{
			TRACE2("%x - CRMSWaveSocket::StartWaveSession().bind(), sin_port=%d\n", this, ntohs(m_saServer.sin_port));
			binded = true;
			break;
		}
	}
	if(!binded)
	{
		TRACE0("Unable to bind udp socket\n");
		CloseSession();
		return 0;
	} else
           if (pSending!=NULL) pSending->SetSocket(m_udpSocket);

	Execute();

	return ntohs(m_saServer.sin_port);
}


void CRMSWaveSocket::CloseSession()
{
	if (m_udpSocket) {
        if (pSending!=NULL) pSending->SetSocket(0);

		LINGER linger = { 1,0 };
		//Fixes Bug in MFC Socket Impl
		setsockopt(m_udpSocket,SOL_SOCKET,SO_LINGER,(LPCTSTR)&linger,sizeof(linger));
		shutdown(m_udpSocket,SD_BOTH);
		//Sleep(100);// need to wait!
		closesocket(m_udpSocket);
		//Sleep(100);// need to wait!
		m_udpSocket=0;
	}

	m_bActive = false;
}
